1 module hip.hipaudio.backend.xaudio.source;
2 
3 version(Windows):
4 version(DirectX):
5 
6 import hip.hipaudio.backend.xaudio.player;
7 import hip.hipaudio.backend.xaudio.clip;
8 import hip.hipaudio.backend.audioclipbase;
9 import hip.hipaudio.audiosource;
10 import directx.xaudio2;
11 import directx.win32;
12 import hip.util.system:getWindowsErrorMessage;
13 import hip.error.handler;
14 
15 enum WAVE_FORMAT_IEEE_FLOAT = 0x0003;
16 
17 class HipXAudioSource : HipAudioSource
18 {
19     IXAudio2SourceVoice sourceVoice;
20     protected bool isClipDirty = true;
21 
22     this(HipXAudioPlayer player)
23     {
24 
25         AudioConfig cfg = player.config;
26         WAVEFORMATEX fmt;
27         WORD format;
28 
29         switch(cfg.getBitDepth)
30         {
31             case 8:
32             case 16:
33                 format = WAVE_FORMAT_PCM;
34                 break;
35             case 32:
36                 format = WAVE_FORMAT_IEEE_FLOAT;
37                 break;
38             default:
39                 ErrorHandler.assertExit(false, "XAudio2 Does not support the current bit depth");
40                 break;
41         }
42         fmt.wFormatTag      = format;
43         fmt.nChannels       = cast(ushort)cfg.channels;
44         fmt.nAvgBytesPerSec = cfg.sampleRate * (cfg.getBitDepth/8) * cfg.channels;
45         fmt.nSamplesPerSec  = cfg.sampleRate;
46         fmt.nBlockAlign     = cast(ushort)(cfg.channels * cfg.getBitDepth())/8;
47         fmt.wBitsPerSample  = cast(ushort)cfg.getBitDepth;
48         fmt.cbSize          = 0;
49         HRESULT hr = player.xAudio.CreateSourceVoice(&sourceVoice, &fmt);
50 
51         ErrorHandler.assertLazyExit(SUCCEEDED(hr), "Could not create source voice: \n\t"~HipXAudioPlayer.getError(hr));
52         
53     }
54     alias clip = HipAudioSource.clip;
55 
56 
57     protected void submitClip()
58     {
59         XAUDIO2_BUFFER* buffer = getBufferFromAPI(clip).xaudio;
60         if(isClipDirty)
61         {
62             isClipDirty = false;
63             HRESULT hr = sourceVoice.SetSourceSampleRate(clip.getSampleRate());
64             ErrorHandler.assertLazyExit(SUCCEEDED(hr),
65             "Could not set source voice Sample Rate:\n\t"~HipXAudioPlayer.getError(hr));
66         }
67 
68         HRESULT hr = sourceVoice.SubmitSourceBuffer(buffer, null);
69         ErrorHandler.assertLazyExit(SUCCEEDED(hr),
70         "Could not submit XAudio2 source voice:\n\t"~HipXAudioPlayer.getError(hr));
71     }
72 
73     override IHipAudioClip clip(IHipAudioClip newClip)
74     {
75         if(newClip != clip)
76             isClipDirty = true;
77         super.clip(newClip);
78         return newClip;
79     }
80 
81     alias loop = HipAudioSource.loop;
82     override bool loop(bool value)
83     {
84         bool ret = super.loop(value);
85         HipXAudioClip c = clip.getAudioClipBackend!(HipXAudioClip);
86         c.buffer.LoopCount = loop ? XAUDIO2_LOOP_INFINITE : 0;
87         return ret;
88     }
89 
90     
91         
92     override bool play()
93     {
94         if(isPlaying)
95         {
96             sourceVoice.Stop();
97             sourceVoice.FlushSourceBuffers();
98         }
99         submitClip();
100         
101         HRESULT hr = sourceVoice.Start(0);
102         ErrorHandler.assertLazyExit(SUCCEEDED(hr), "XAudio2 Failed to play: \n\t"~HipXAudioPlayer.getError(hr));
103         isPlaying = true;        
104         return SUCCEEDED(hr);
105     }
106     override bool stop()
107     {
108         //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too.
109         auto hr = sourceVoice.Stop(XAUDIO2_PLAY_TAILS);
110         ///Makes it return to 0
111         sourceVoice.FlushSourceBuffers();
112         debug
113             ErrorHandler.assertLazyErrorMessage(SUCCEEDED(hr), "XAudio2 stop failure", HipXAudioPlayer.getError(hr));
114 
115         isPlaying = false;
116         return SUCCEEDED(hr);
117     }
118     override bool pause()
119     {
120         isPaused = true;
121         //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too.
122         return SUCCEEDED(sourceVoice.Stop(XAUDIO2_PLAY_TAILS));
123     }
124     override bool play_streamed() => false;
125     
126 
127     ~this()
128     {
129         if(sourceVoice !is null)
130         {
131             sourceVoice.DestroyVoice();
132             sourceVoice = null;
133         }
134     }
135 }